package com.wechat.network.robot;

import java.lang.reflect.Method;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.CompletionStage;

import com.swak.exception.FluxInvokeException;
import com.swak.reactivex.threads.Context;
import com.swak.reactivex.threads.Contexts;
import com.swak.utils.StringUtils;
import com.swak.vertx.invoker.GenericFluxInvoker;
import com.wechat.annotation.Robot;
import com.wechat.command.Command;
import com.wechat.command.CommandResult;
import com.wechat.config.RobotBean;
import com.wechat.network.AbstractNetwork;
import com.wechat.network.Network;
import com.wechat.network.NetworkChannel;
import com.wechat.network.NetworkHandler;
import com.wechat.network.robot.inout.RobotInChannel;
import com.wechat.network.robot.inout.RobotOutChannel;

public class RobotNetwork extends AbstractNetwork implements Network {

	private final RobotOutChannel out;
	private final RobotInChannel in;

	private final RobotBean robot;
	protected final Object service;
	protected final Method method;
	protected final GenericFluxInvoker invoker;

	public RobotNetwork(RobotBean bean, NetworkHandler handler) {
		Robot robot = bean.getRobot();
		Object service = bean.getService();

		Method executeMethod = null;
		Method[] methods = service.getClass().getMethods();
		for (Method method : methods) {
			if (StringUtils.isNotBlank(robot.method()) && method.getName().equals(robot.method())) {
				executeMethod = method;
			}
		}

		this.robot = bean;
		this.service = service;
		this.method = executeMethod;
		this.invoker = new GenericFluxInvoker(this.service);
		this.out = new RobotOutChannel(this);
		this.in = new RobotInChannel(this, this.out, handler);
	}

	@Override
	public String name() {
		return robot.getId();
	}

	@Override
	public CompletionStage<CommandResult> send(Command message) {

		if (logger.isDebugEnabled()) {
			logger.debug("向 [机器人 {}] 发送消息 {}", this.robot.getName(), message.getData());
		}

		try {
			NetworkChannel exchangeChannel = NetworkChannel.getOrAddChannel(this, in);
			Object[] args = new Object[] { exchangeChannel, message };
			return this.invoke(args);
		} catch (Throwable e) {
			RuntimeException error = new FluxInvokeException(
					String.format("Failed to Invoke [Service:{%s} - Method: {%s}].", service.getClass().getName(),
							method.getName()),
					e.getCause() != null ? e.getCause() : e);
			logger.error("[机器人 {}] 发送消息 失败， {}", this.robot.getName(), error);
			throw error;
		}
	}

	@SuppressWarnings("unchecked")
	protected CompletionStage<CommandResult> invoke(Object[] args) throws Throwable {
		Context context = Contexts.currentContext();
		Object result = this.invoker.doInvoke(this.method, args);
		if (result != null && result instanceof CompletionStage && context != null) {
			CompletionStage<Object> resultFuture = (CompletionStage<Object>) result;
			CompletableFuture<CommandResult> future = new CompletableFuture<>();
			resultFuture.whenComplete((v, e) -> {
				context.runOnContext(() -> {
					if (e != null) {
						future.completeExceptionally(e);
					} else {
						future.complete(CommandResult.wrap(v));
					}
				});
			});
			return future;
		} else if (result != null && result instanceof CompletionStage) {
			CompletionStage<Object> resultFuture = (CompletionStage<Object>) result;
			CompletableFuture<CommandResult> future = new CompletableFuture<>();
			resultFuture.whenComplete((v, e) -> {
				if (e != null) {
					future.completeExceptionally(e);
				} else {
					future.complete(CommandResult.wrap(v));
				}
			});
			return future;
		}
		return CompletableFuture.completedStage(CommandResult.wrap(result));
	}
}
